const PI = Math.PI
const A = 6378245.0
const EE = 0.00669342162296594323
const X_PI = PI * 3000.0 / 180.0

function outOfChina(lat: number, lon: number): boolean {
  return lon < 72.004 || lon > 137.8347 || lat < 0.8293 || lat > 55.8271
}

function transformLat(x: number, y: number): number {
  return -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y +
         0.1 * x * y + 0.2 * Math.sqrt(Math.abs(x)) +
         (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0 +
         (20.0 * Math.sin(y * PI) + 40.0 * Math.sin(y / 3.0 * PI)) * 2.0 / 3.0 +
         (160.0 * Math.sin(y / 12.0 * PI) + 320 * Math.sin(y * PI / 30.0)) * 2.0 / 3.0
}

function transformLon(x: number, y: number): number {
  return 300.0 + x + 2.0 * y + 0.1 * x * x +
         0.1 * x * y + 0.1 * Math.sqrt(Math.abs(x)) +
         (20.0 * Math.sin(6.0 * x * PI) + 20.0 * Math.sin(2.0 * x * PI)) * 2.0 / 3.0 +
         (20.0 * Math.sin(x * PI) + 40.0 * Math.sin(x / 3.0 * PI)) * 2.0 / 3.0 +
         (150.0 * Math.sin(x / 12.0 * PI) + 300.0 * Math.sin(x / 30.0 * PI)) * 2.0 / 3.0
}

/** WGS-84 → GCJ-02 */
export function wgs84ToGcj02(lat: number, lon: number): [number, number] {
  if (outOfChina(lat, lon)) return [lat, lon]
  let dLat = transformLat(lon - 105.0, lat - 35.0)
  let dLon = transformLon(lon - 105.0, lat - 35.0)
  const radLat = lat / 180.0 * PI
  let magic = Math.sin(radLat)
  magic = 1 - EE * magic * magic
  const sqrtMagic = Math.sqrt(magic)
  dLat = (dLat * 180.0) / ((A * (1 - EE)) / (magic * sqrtMagic) * PI)
  dLon = (dLon * 180.0) / (A / sqrtMagic * Math.cos(radLat) * PI)
  const mgLat = lat + dLat
  const mgLon = lon + dLon
  return [mgLat, mgLon]
}

/** GCJ-02 → WGS-84（近似还原） */
export function gcj02ToWgs84(lat: number, lon: number): [number, number] {
  if (outOfChina(lat, lon)) return [lat, lon]
  const [mgLat, mgLon] = wgs84ToGcj02(lat, lon)
  const dLat = mgLat - lat
  const dLon = mgLon - lon
  return [lat - dLat, lon - dLon]
}

/** GCJ-02 → BD-09 */
export function gcj02ToBd09(lat: number, lon: number): [number, number] {
  const z = Math.sqrt(lon * lon + lat * lat) + 0.00002 * Math.sin(lat * X_PI)
  const theta = Math.atan2(lat, lon) + 0.000003 * Math.cos(lon * X_PI)
  const bdLon = z * Math.cos(theta) + 0.0065
  const bdLat = z * Math.sin(theta) + 0.006
  return [bdLat, bdLon]
}

/** BD-09 → GCJ-02 */
export function bd09ToGcj02(lat: number, lon: number): [number, number] {
  const x = lon - 0.0065
  const y = lat - 0.006
  const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * X_PI)
  const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * X_PI)
  const gcjLon = z * Math.cos(theta)
  const gcjLat = z * Math.sin(theta)
  return [gcjLat, gcjLon]
}

/** WGS-84 → BD-09（两步：WGS84 → GCJ02 → BD09） */
export function wgs84ToBd09(lat: number, lon: number): [number, number] {
  const [gcjLat, gcjLon] = wgs84ToGcj02(lat, lon)
  return gcj02ToBd09(gcjLat, gcjLon)
}

/** BD-09 → WGS-84 */
export function bd09ToWgs84(lat: number, lon: number): [number, number] {
  const [gcjLat, gcjLon] = bd09ToGcj02(lat, lon)
  return gcj02ToWgs84(gcjLat, gcjLon)
}
